package com.xiyuan.smartutils.extend;

import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;

/**
 * the description for class Tree
 *
 * @author xiyuan-lgz 2023-09-05 @version v1.0.0 新建与整理
 */
public final class Tree<T> {
    
    private final Serializable id;
    private final Serializable parentId;
    private transient Tree<T> parent;
    private int level = 1;
    private int sort;
    private final T node;
    private List<Tree<T>> children;
    
    public Object getId() {
        return id;
    }
    
    public Object getParentId() {
        return parentId;
    }
    
    public int getLevel() {
        return level;
    }
    
    public T getNode() {
        return node;
    }
    
    public int getSort() {
        return sort;
    }
    
    public void setSort(int sort) {
        this.sort = sort;
    }
    
    public List<Tree<T>> getChildren() {
        return children;
    }
    
    public Tree(T item, Serializable key, Serializable parentKey) {
        this.node = item;
        this.id = key;
        this.parentId = parentKey;
    }
    
    public void addChildren(Tree<T> subtree) {
        if (subtree == null) {return;}
        
        if (children == null) {children = new ArrayList<Tree<T>>();}
        if (subtree == this) {return;}
        // 检查闭环问题
        Tree<T> parent = this;
        while ((parent = parent.parent) != null) {
            if (parent == subtree) {throw new IllegalArgumentException("Cannot add parent node as child node");}
        }
        
        
        subtree.parent = this;
        children.add(subtree);
        subtree.sort = children.size();
        subtree.setLevel(level + 1);
        subtree.resetChildLevel(subtree);
    }
    
    private void resetChildLevel(Tree<T> parent) {
        List<Tree<T>> children = parent.children;
        if (children != null) {children.forEach(child -> resetChildLevel(child.setLevel(parent.level + 1)));}
    }
    
    /**
     * remove all children
     *
     * @return removed children
     */
    private List<Tree<T>> removeChildren() {
        List<Tree<T>> children = this.children;
        this.children = null;
        return children;
    }
    
    private Tree<T> setLevel(int level) {
        this.level = level;
        return this;
    }
    
    
    /**
     * 构建树形节点
     *
     * @param list            数据列表
     * @param keyGetter       节点ID, 需要注意的是
     * @param parentKeyGetter 父节点ID,
     * @param <T>             返回更节点列表
     * @return
     */
    public static <T> List<Tree<T>> buildTree(Collection<T> list,
                                              KeyGetter<T> keyGetter,
                                              KeyGetter<T> parentKeyGetter) {
        if (list == null || list.isEmpty()) {return new ArrayList<>(0);}
        
        Map<Object, Tree<T>> treeMap = new LinkedHashMap<>();
        list.forEach(item -> {
            String key = String.valueOf(keyGetter.key(item));
            String parentKey = String.valueOf(parentKeyGetter.key(item));
            treeMap.put(key, new Tree<T>(item, key, parentKey));
        });
        
        Collection<Tree<T>> vls = treeMap.values();
        treeMap.forEach((k, v) -> {
            Tree<T> parent = treeMap.get(v.getParentId());
            if (parent != null && parent != v) {parent.addChildren(v);}
        });
        
        
        return vls.stream().filter(r -> r.level == 1).collect(Collectors.toList());
    }
    
    /**
     * 拆解树形节点。返回所树节点列表
     *
     * @param roots 根节点列表
     * @return 所有节点
     */
    public static <T> List<Tree<T>> treeToArray(Collection<Tree<T>> roots) {
        return treeToArray(roots, false);
    }
    
    /**
     * 拆解树形节点。返回所树节点列表
     *
     * @param roots           树节点
     * @param reserveChildren 是否保留子树结构，false表示不保留，返回的tree列表中 children节点为null
     * @param <T>             原始类型
     * @return 树节点列表
     */
    public static <T> List<Tree<T>> treeToArray(Collection<Tree<T>> roots, boolean reserveChildren) {
        if (roots == null) {return new ArrayList<>(0);}
        List<Tree<T>> ls = new ArrayList<Tree<T>>();
        roots.forEach(item -> {
            ls.add(item);
            List<Tree<T>> children = reserveChildren ? item.children : item.removeChildren();
            if (children != null && !children.isEmpty()) {
                ls.addAll(treeToArray(item.children));
            }
        });
        
        return ls;
    }
    
    /**
     * 拆解树形节点。返回所树节点的原始数据列表
     *
     * @param roots 根节点列表
     * @return 原始数据列表
     */
    public static <T> List<T> treeToDataArray(List<Tree<T>> roots) {
        if (roots == null) {return new ArrayList<>(0);}
        return treeToArray(roots).stream().map(Tree::getNode).collect(Collectors.toList());
    }
    
    
    private static <T> List<Tree<T>> filter(List<Tree<T>> list, Tree<T> parent, TreeFilter<T> filter) {
        return list.stream().filter(r -> {
            if (r.children != null) {r.children = filter(r.children, r, filter);}
            return filter.filter(r, parent);
        }).collect(Collectors.toList());
    }
    
    /**
     * 过滤树
     *
     * @param list   数列表
     * @param filter 过滤器
     * @return Returns a stream consisting of the elements of this stream that match the given filter
     */
    public static <T> List<Tree<T>> filter(List<Tree<T>> list, TreeFilter<T> filter) {
        
        return list.stream().filter(r -> {
            if (r.children != null) {r.children = filter(r.children, r, filter);}
            return filter.filter(r, null);
        }).collect(Collectors.toList());
    }
    
    @FunctionalInterface
    public static interface KeyGetter<T> {
        Serializable key(T item);
    }
    
    @FunctionalInterface
    public static interface TreeFilter<T> {
        boolean filter(Tree<T> item, Tree<T> parent);
    }
}
